package ua.kharkiv.karimov.knure.SummaryTask1.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import ua.kharkiv.karimov.knure.SummaryTask1.server.Server.aircraft_record;

class ServeOneThread extends Thread {

	private Socket socket;

	private BufferedReader in;

	private PrintWriter out;

	public ServeOneThread(Socket s) throws IOException {
		socket = s;
		in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
				socket.getOutputStream())), true);
		start();
	}

	public void run() {
		try {
			if (socket.isClosed())
				return;
			String str = in.readLine();
			String[] temp = str.split("\t");
			final String myName = temp[0];
			if (temp.length > 5) {
				aircraft_record ar = new aircraft_record();
				ar.name = myName;

				ar.capacity = Double.parseDouble(temp[1]);

				ar.fuel = Double.parseDouble(temp[2]);

				ar.fuelPerKm = Double.parseDouble(temp[3]);

				ar.maxWeight = Double.parseDouble(temp[4]);

				ar.weight = Double.parseDouble(temp[5]);

				Server.aircraft.add(ar);
			}
			// ==============SORTING====================
			for (int i = 0; i < Server.aircraft.size(); i++)
				for (int j = i; j < Server.aircraft.size(); j++) {
					aircraft_record ar1 = Server.aircraft.get(i);
					aircraft_record ar2 = Server.aircraft.get(j);
					double range1 = ar1.fuel / ar1.fuelPerKm;
					double range2 = ar2.fuel / ar2.fuelPerKm;
					if (range1 > range2) {
						Server.aircraft.set(i, ar2);
						Server.aircraft.set(j, ar1);
					}
				}
			// =========================================

			System.out.println(socket.getInetAddress() + "(" + myName
					+ "): Client connected");
			Server.messages.put(myName, new HashMap<String, String>());

			// thread send messages
			new Thread() {
				public void run() {
					while (!socket.isClosed()) {
						HashMap<String, String> messages = Server.messages
								.get(myName);
						// check messages for myName
						synchronized (messages) {
							for (Entry<String, String> mess : messages
									.entrySet()) {
								System.out
										.println(socket.getInetAddress() + "("
												+ myName + ") SEND: "
												+ mess.getKey() + " "
												+ mess.getValue());
								out.println(mess.getKey() + " "
										+ mess.getValue());
							}
							messages.clear();
						}

						// check messages for all
						messages = Server.messages.get("*");
						synchronized (messages) {
							for (Entry<String, String> mess : messages
									.entrySet()) {
								if (mess.getValue().equals("update")) {
									for (Entry<String, HashMap<String, String>> mess2 : Server.messages
											.entrySet()) {
										mess2.getValue().put(mess.getKey(),
												mess.getValue());
									}
								}
							}
							messages.clear();
						}
					}
					try {
						sleep(100);
					} catch (InterruptedException e) {
					}
				}
			}.start();

			while (true) {
				str = in.readLine();
				System.out.println(socket.getInetAddress() + "(" + myName
						+ ") : " + str);
				if (str == null || str.toLowerCase().equals("exit"))
					break;
				if (str.indexOf("get") > -1) {
					String[] tmp = str.split(" ");
					if (tmp.length > 2) {
						if (tmp[1].equals("fuelkm") && tmp.length > 3) {
							for (aircraft_record ar : Server.aircraft)
								if (ar.fuelPerKm > Integer.parseInt(tmp[2])
										&& ar.fuelPerKm < Integer
												.parseInt(tmp[3])) {
									HashMap<String, String> messages = Server.messages
											.get(myName);
									if (messages == null)
										continue;
									synchronized (messages) {
										messages.put("", "fuelPerKm [" + tmp[2]
												+ "; " + tmp[3] + "]: "
												+ ar.name);
									}
								}
						}
					}
					// ===========weight_capacity=========
				} else if (str.equals("weight_capacity")) {
					double wt = 0;
					double cap = 0;
					for (aircraft_record ar : Server.aircraft) {
						wt += ar.weight;
						cap += ar.capacity;
					}
					wt /= Server.aircraft.size();
					cap /= Server.aircraft.size();
					String mess = "Total capacity = " + wt + "; "
							+ "the total carrying capacity of " + cap;
					HashMap<String, String> messages = Server.messages
							.get(myName);
					if (messages == null)
						continue;
					System.out.println(socket.getInetAddress() + "(" + myName
							+ ") PUT: " + mess);
					synchronized (messages) {
						messages.put("", mess);
					}
					// ==============see=============
				} else if (str.toLowerCase().indexOf("see") == 0
						&& str.length() > 4) {
					String[] mess = str.split(" ");
					if (mess.length > 1) {
						HashMap<String, String> messages = Server.messages
								.get(mess[1]);
						if (messages == null)
							continue;
						System.out.println(socket.getInetAddress() + "("
								+ myName + ") PUT: " + mess[1] + " " + mess[0]);
						synchronized (messages) {
							messages.put(myName, mess[0]);
						}
					}
					// =============update==============
				} else if (str.equals("update")) {
					HashMap<String, String> messages = Server.messages.get("*");
					if (messages == null)
						continue;
					synchronized (messages) {
						messages.put(myName, str);
					}
					// ========Send messages============
				} else if (str.indexOf(" ") > 0) {
					String[] mess = str.split(" see:");
					if (mess.length > 1) {
						HashMap<String, String> messages = Server.messages
								.get(mess[0]);
						if (messages == null)
							continue;
						System.out.println(socket.getInetAddress() + "("
								+ myName + ") PUT: " + mess[0] + " " + mess[1]);

						synchronized (messages) {
							messages.put(myName, mess[1]);
						}
					}
				}
				try {
					sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
			System.out.println("closing...");
		} catch (IOException e) {
			System.err.println("IO Exception");
		} finally {
			try {
				socket.close();
			} catch (IOException e) {
				System.err.println("Socket not closed");
			}
		}
	}
}

/**
 * The server controls the aircraft - receives data from them, and they may send different requests. A server also handles requests from the client. Basic commands:
 * <li> weight_capacity - Calculate the total capacity and capacity. </li>
 * <li> get fuelkm xy - Find plane in the company, corresponding to a specified range of parameters in fuel consumption. x y range </li>
 * @author Karimov
 */
public class Server {

	public static final int PORT = 8888;
	protected ServerSocket socket;
	protected static HashMap<String, HashMap<String, String>> messages = new HashMap<String, HashMap<String, String>>();
	protected static List<aircraft_record> aircraft = new ArrayList<aircraft_record>();

	protected static class aircraft_record {
		public Double fuelPerKm = null;
		public Double fuel = null;
		public Double weight = null;
		public Double maxWeight = null;
		public Double capacity = null;
		public String name = null;

		public aircraft_record() {

		}
	}

	public Server() throws IOException {
		messages.put("*", new HashMap<String, String>());
		socket = new ServerSocket(PORT);
		System.out.println("Server Started");
		try {
			while (true) {
				Socket clsocket = socket.accept();
				try {
					new ServeOneThread(clsocket);
				} catch (IOException e) {
					clsocket.close();
				}
			}
		} finally {
			socket.close();
		}
	}

	public static void main(String[] args) throws IOException {
		new Server();
	}
}
